👆建議你可以使用影片子母畫面功能或全螢幕播放來獲得最佳的觀賞體驗,👇下方是本篇教學的相關筆記。
在 pages 目錄下建立一個檔案 ./pages/index.vue:
<template>
<div class="flex flex-col items-center justify-center px-4 py-6">
<div class="flex w-full max-w-md flex-col justify-center">
<div class="flex flex-col items-center">
<h2 class="my-4 text-center text-3xl font-bold tracking-tight text-gray-700">Cookie</h2>
</div>
<div class="mx-auto flex w-full max-w-xs flex-row justify-around gap-2">
<div class="flex flex-col items-center gap-2">
<button
type="button"
class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
@click="setNameCookie"
>
設置 name
</button>
<div class="flex">
<label class="text-lg font-semibold text-emerald-500">name:</label>
<span class="ml-2 flex text-lg text-slate-700">{{ name }}</span>
</div>
</div>
<div class="flex flex-col items-center gap-2">
<button
type="button"
class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
@click="setCounterCookie"
>
設置 counter
</button>
<div class="flex">
<label class="text-lg font-semibold text-emerald-500">counter:</label>
<span class="ml-2 flex text-lg text-slate-700">{{ counter }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
const name = useCookie('name')
const counter = useCookie('counter', { maxAge: 10 })
const setNameCookie = () => {
name.value = 'Ryan'
}
const setCounterCookie = () => {
counter.value = Math.round(Math.random() * 1000)
}
</script>
修改 app.vue 檔案:
<template>
<div>
<NuxtPage />
</div>
</template>
在 server/api/ 目錄下建立一個檔案 ./server/api/counter.get.js:
export default defineEventHandler((event) => {
let counter = getCookie(event, 'counter')
counter = parseInt(counter, 10) || 0
counter += 1
setCookie(event, 'counter', counter)
return { counter }
})
在 pages 目錄下建立一個檔案 ./pages/index.vue:
<template>
<div class="flex flex-col items-center justify-center px-4 py-6">
<div class="flex w-full max-w-md flex-col justify-center">
<div class="flex flex-col items-center">
<h2 class="my-4 text-center text-3xl font-bold tracking-tight text-gray-700">Cookie</h2>
</div>
<div class="mx-auto flex w-full max-w-xs flex-row justify-around gap-2">
<div class="flex flex-col items-center gap-2">
<button
type="button"
class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
@click="setNameCookie"
>
設置 name
</button>
<div class="flex">
<label class="text-lg font-semibold text-emerald-500">name:</label>
<span class="ml-2 flex text-lg text-slate-700">{{ name }}</span>
</div>
</div>
<div class="flex flex-col items-center gap-2">
<button
type="button"
class="w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
@click="setCounterCookie"
>
設置 counter
</button>
<div class="flex">
<label class="text-lg font-semibold text-emerald-500">counter:</label>
<span class="ml-2 flex text-lg text-slate-700">{{ counter }}</span>
</div>
</div>
</div>
<div class="mt-2 flex w-full max-w-md flex-col items-center">
<button
type="button"
class="mt-2 w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
@click="sendRequest"
>
發送 API 請求至 /api/counter
</button>
</div>
</div>
</div>
</template>
<script setup>
const name = useCookie('name')
const counter = useCookie('counter', { maxAge: 10 })
const setNameCookie = () => {
name.value = 'Ryan'
}
const setCounterCookie = () => {
counter.value = Math.round(Math.random() * 1000)
}
const sendRequest = () => {
useFetch('/api/counter', {
headers: useRequestHeaders(['cookie'])
})
}
</script>
修改 app.vue 檔案:
<template>
<div>
<NuxtPage />
</div>
</template>
安裝 jsonwebtoken 套件
npm install -D jsonwebtoken
在 server/api/login 目錄下建立一個檔案 ./server/api/login.post.js:
import jwt from 'jsonwebtoken'
export default defineEventHandler(async (event) => {
const body = await readBody(event)
if (!body.name === 'Ryan') {
throw createError({
statusCode: 400,
statusMessage: '登入失敗'
})
}
const jwtTokenPayload = {
id: 1,
nickname: 'Ryan',
email: 'ryanchien8125@gmail.com'
}
const maxAge = 60 * 60 * 24 * 7
const expires = Math.floor(Date.now() / 1000) + maxAge
const jwtToken = jwt.sign(
{
exp: expires,
data: jwtTokenPayload
},
'JWT_SIGN_SECRET_PLEASE_REPLACE_WITH_YOUR_KEY'
)
setCookie(event, 'access_token', jwtToken, {
maxAge,
expires: new Date(expires * 1000),
secure: true,
httpOnly: true,
path: '/'
})
return '登入成功'
})
在 pages 目錄下建立一個檔案 ./pages/login.vue:
<template>
<div class="flex flex-col items-center justify-center px-4 py-6">
<div class="flex w-full max-w-md flex-col justify-center">
<div class="flex flex-col items-center">
<h2 class="my-4 text-center text-3xl font-bold tracking-tight text-gray-700">Login</h2>
</div>
<div class="mt-2 flex w-full max-w-md flex-col items-center">
<button
type="button"
class="mt-2 w-fit rounded-sm bg-emerald-500 px-4 py-2 text-sm text-white hover:bg-emerald-600 focus:outline-none focus:ring-2 focus:ring-emerald-400 focus:ring-offset-2"
@click="sendLoginRequest"
>
發送 API 請求至 /api/login
</button>
</div>
</div>
</div>
</template>
<script setup>
const sendLoginRequest = () => {
useFetch('/api/login', {
method: 'POST',
body: {
name: 'Ryan'
}
})
}
</script>
在 server/api 目錄下建立一個檔案 ./server/api/whoami.get.js:
import jwt from 'jsonwebtoken'
export default defineEventHandler((event) => {
const jwtToken = getCookie(event, 'access_token')
try {
const { data: userInfo } = jwt.verify(jwtToken, 'JWT_SIGN_SECRET_PLEASE_REPLACE_WITH_YOUR_KEY')
return {
id: userInfo.id,
nickname: userInfo.nickname,
email: userInfo.email
}
} catch (e) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized'
})
}
})
在 pages 目錄下建立一個檔案 ./pages/whoami.vue:
<template>
<div class="flex flex-col items-center justify-center px-4 py-6">
<div class="w-full max-w-md">
<div class="flex flex-col items-center">
<h2 class="mt-6 text-center text-3xl font-bold tracking-tight text-gray-700">我是誰</h2>
</div>
</div>
<div class="mt-8 flex w-full max-w-xs flex-col">
<div v-if="error" class="text-center text-rose-500">{{ error }}</div>
<template v-if="userInfo">
<div v-for="key in Object.keys(userInfo)" :key="key" class="mt-1 flex flex-wrap break-all">
<label class="text-lg font-semibold text-emerald-500"> {{ key }}:</label>
<span class="ml-2 flex flex-1 text-lg text-slate-700">{{ userInfo[key] }}</span>
</div>
</template>
</div>
</div>
</template>
<script setup>
const { data: userInfo, error } = await useFetch('/api/whoami', {
headers: useRequestHeaders(['cookie'])
})
</script>
感謝大家的閱讀,歡迎大家給予建議與討論,也請各位大大鞭小力一些:)
如果對這個 Nuxt 3 系列感興趣,可以訂閱
接收通知,也歡迎分享給喜歡或正在學習 Nuxt 3 的夥伴。
範例程式碼
參考資料